package clientcache

import (
    "cing.io/cing-api/planner"
    builtins "cing.io/cing/internal/builtins/tasktype"
    "cing.io/cing/internal/config"
    "cing.io/cing/internal/storage"
    "cing.io/cing/pkg/util"
    "fmt"
    log "github.com/sirupsen/logrus"
    "google.golang.org/grpc"
    "google.golang.org/grpc/balancer/roundrobin"
    "google.golang.org/grpc/credentials/insecure"
    "sync"
)

var Planner *plannerClientCache

type plannerClientCache struct {
    holderMap map[string]*plannerHolder // key: svc, val: holder
    m         sync.Mutex                // mutex for key setting
}

type plannerHolder struct {
    m   sync.Mutex // cli init lock
    cli planner.CingPlannerClient
}

func (cache *plannerClientCache) GetPlanner(taskTypeName string) (client planner.CingPlannerClient) {
    // get planner cli for builtin task type
    client = builtins.GetPlanner(taskTypeName)
    if client != nil {
        return
    }

    // get custom task type planner cli
    taskType := storage.GetTaskType(taskTypeName)
    log.Debug(taskType)

    client, err := cache.GetPlannerInService(util.SplitNamespaceAndName(taskType.PlannerService))
    if err != nil {
        log.Error("get planner client err:", err)
    }
    return
}

func (cache *plannerClientCache) GetPlannerInService(namespace, service string) (client planner.CingPlannerClient, err error) {
    plannerService := util.MergeNamespaceAndName(namespace, service)
    holder := cache.holderMap[plannerService]
    if holder == nil {
        cache.m.Lock()
        if holder = cache.holderMap[plannerService]; holder == nil {
            holder = &plannerHolder{}
            cache.holderMap[plannerService] = holder
        }
        cache.m.Unlock()
    }
    client = holder.cli
    if client == nil {
        holder.m.Lock()
        if client = holder.cli; client == nil {
            client, err = initPlanner(namespace, service)
            holder.cli = client
        }
        holder.m.Unlock()
    }
    return
}

func initPlanner(namespace, service string) (planner.CingPlannerClient, error) {
    var target string

    if config.LocalTest {
        target = fmt.Sprintf("%s:%d", config.Localhost, config.PlannerPort)
    } else {
        if namespace == "" {
            return nil, fmt.Errorf("namespace cannot be empty")
        }
        // use dns lookup
        target = fmt.Sprintf("dns:///%s.%s.svc.%s:%d",
            service, namespace, config.ClusterDomainName, config.PlannerPort)
    }

    conn, err := grpc.Dial(
        target,
        grpc.WithTransportCredentials(insecure.NewCredentials()),
        grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"loadBalancingConfig": [{"%s":{}}]}`, roundrobin.Name)),
    )
    if err != nil {
        return nil, err
    }

    return planner.NewCingPlannerClient(conn), nil
}
